#ifndef G3_FSTREAM_H
#define G3_FSTREAM_H

#include <string.h>
#include <stdlib.h>
#include <string>

#include "types.h"
#include "log.h"

// Accumulative buffer - no data is freed
#define STREAM_ACCUM_BUF		1
// Default Automatic buffer - read data is freed

#define STREAM_NO_TOTAL_SIZE	2

// Default Maximum for unflushed data
#define STREAM_DEF_UNFLUSHED	65536

namespace G3 {

using std::string;

enum STREAM_SEEK_FLAG {
	STREAM_BEGINNING = 0,
	STREAM_SET = 1,
	STREAM_GET = 1,
	STREAM_END = 2,
	STREAM_SIZE = 3,
	STREAM_MOVE = 4,
	STREAM_FILE_SIZE = 5,
	STREAM_SET_FILE = 6
};

class FILESTREAM {
	public:
		FILESTREAM ();
		~FILESTREAM ();

// User should set this pointer to another G3::LOG object when necessary
// By default, Log points to G3::MainLog (file "engine.log")
		G3::LOG *Log;

// Contains the last line separator / delimiter that was found
		char LastLineDelimiter;

		string LastFilename;

		const void *GetDataPointer ();

//==============================================================================
// Sets stream caret position
//
//		long Offset				File position to seek for
//
//		STREAM_SEEK_FLAG Flag
//			STREAM_BEGINNING	File position caret is reset to the beginning of
//								current stream buffer
//			STREAM_SET			Sets file position to Offset from the beginning
//								of the buffered data
//			STREAM_END			File position caret is moved to the end of the
//								stream
//
//	Return true on success
//==============================================================================
		bool Seek (long Offset, STREAM_SEEK_FLAG Flag);

//==============================================================================
// Tells the stream caret position
//
//		STREAM_SEEK_FLAG Flag
//			STREAM_BEGINNING	Tells the BufferPosition of the stream
//
//			STREAM_GET			Tells the CaretPosition of the stream
//
//			STREAM_END			Tells the TotalSize of the stream
//
//			STREAM_SIZE			Tells the BufferedSize of the stream
//
//	Returns the position in bytes
//==============================================================================
		long Tell (STREAM_SEEK_FLAG Flag);

//==============================================================================
// Returns true if End Of File was encountered
//==============================================================================
		bool EndOfFile ();

//==============================================================================
// Just sets the total size of the stream (in bytes)
//==============================================================================
		void SetTotalSize (long Size);

//==============================================================================
// Sets the start of the file
//==============================================================================
		void SetFileStart ();

//==============================================================================
// Just sets the size current file (in bytes)
//==============================================================================
		void SetFileSize (long Size);

//==============================================================================
// Sets the limit for unflushed data. Regulates the minimum operative memory
// required for streaming. When more then Limit bytes data is already read
// and not needed anymore, it flushes.
//
//		long Limit					The limit should be big enough so that
//									the slow Flush () function is not called
//									too often. On the other hand, when the limit
//									is too heigh, the user has to manually Flush
//									the stream when it uses up too much memory..
//			STREAM_DEF_UNFLUSHED	This is the default limit 65536 (64 KB)
//==============================================================================
		void SetUnflushedLimit (long Limit);

//==============================================================================
// Reads data from the stream
//
//		void *Pointer			Pointer to a memory block where the data is stored
//
//		long Size				Number of bytes read from the stream
//
//		bool FlushBuffer		Is the stream buffer flushed after reading?
//				true			The part of the buffer that is already read, is freed
//				false			The data already read is still stored
//
//	NOTE:: FlushBuffer has no effect when STREAM_ACCUM_BUF flag is set
//
//	Returns the number of bytes read
//==============================================================================
		long Read (void *Pointer, long Size, bool FlushBuffer = false);

//==============================================================================
// Reads a line from the stream. End of a line can be overridden..
//
//		char *Delimiters		End of line characters
//				"\r\n"			This is the default
//
//		bool FlushBuffer		Is the stream buffer flushed after reading?
//				true			The part of the buffer that is already read, is freed
//				false			The data already read is still stored
//
//	NOTE:: FlushBuffer has no effect when STREAM_ACCUM_BUF flag is set
//
//	Returns, what it was able to read from the stream until \r\n or EOF
//==============================================================================
		string Readline (string Delimiters = "\r\n", bool FlushBuffer = true);

//==============================================================================
// Reads data from the stream and saves it into a file
//
//		const char *Filename	Name of the file to store the data in
//
//		bool FlushBuffer		Is the stream buffer flushed after reading?
//				true			The part of the buffer that is already read, is freed
//				false			The data already read is still stored
//
//		long Size				Number of bytes read from the stream
//				-1, 0			Reads the whole stream buffer
//
//		long Position			Where to write into the file
//
//	NOTE:: FlushBuffer has no effect when STREAM_ACCUM_BUF flag is set
//
//	Returns the number of bytes read
//==============================================================================
		long ReadToFile (const char *Filename, bool FlushBuffer = true, long Size = -1, long Position = 0);

//==============================================================================
// Flushes the stream (has no effect if STREAM_ACCUM_BUF flag is set)
// Frees the part of the buffer that is already read
//
// NOTE:: Very slow
// TODO:: Optimize
//==============================================================================
		bool Flush (bool Force = false);

//==============================================================================
// Adds to the stream buffer
//
//		void *Pointer			Pointer to a memory block from where to buffer
//
//		long Size				Number of bytes to buffer
//
//	Returns Size on success
//==============================================================================
		long Buffer (void *Pointer, long Size);

//==============================================================================
// Buffers a file to the stream
//
//		const char *Filename	Name of the file to buffer
//
//		long Size				Number of bytes to buffer
//				-1, 0			Buffers the whole file into the stream
//
//		long Position			Where in the file should we start reading
//
//	Returns true on success
//==============================================================================
		long BufferFromFile (const char *Filename, long Size = -1, long Position = 0);

//==============================================================================
// Enables a flag
//
//		byte Flag
//				STREAM_ACCUM_BUF	The whole file is stored in the memory
//==============================================================================
		void Enable (byte Flag);

//==============================================================================
// Disables a flag
//==============================================================================
		void Disable (byte Flag);

	protected:
		void *Data;

		// Where in the stream are we at?
		long CaretPosition;

		// Where does the buffered area start?
		long BufferPosition;

		// How much is currently buffered?
		long BufferedSize;

		// Number of bytes buffered in total
		long TotalSize;

		// The size of the current file
		long FileSize;

		// Where did the file start?
		long FileStart;

		long MaximalUnflushed;

        // Block reading from the buffer
		bool BufferReadLocked;

		// Block writing to the buffer
		bool BufferWriteLocked;

		// Lock seeking
		bool CaretLocked;

		byte Flags;
};

long GetFileSize (const char *filename);

}

#endif
